1 package org.apache.torque.engine.database.model;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Hashtable;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import org.apache.commons.collections.map.ListOrderedMap;
29 import org.apache.commons.lang.StringUtils;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import org.apache.torque.engine.EngineException;
35
36 import org.xml.sax.Attributes;
37
38 /***
39 * Data about a table used in an application.
40 *
41 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
42 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
43 * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
44 * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
45 * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
46 * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
47 * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
48 * @version $Id: Table.java 473814 2006-11-11 22:30:30Z tv $
49 */
50 public class Table implements IDMethod
51 {
52 /*** Logging class from commons.logging */
53 private static Log log = LogFactory.getLog(Table.class);
54
55
56 private List columnList;
57 private List foreignKeys;
58 private List indices;
59 private List unices;
60 private List idMethodParameters;
61 private String name;
62 private String description;
63 private String javaName;
64 private String idMethod;
65 private String javaNamingMethod;
66 private Database tableParent;
67 private List referrers;
68 private List foreignTableNames;
69 private boolean containsForeignPK;
70 private Column inheritanceColumn;
71 private boolean skipSql;
72 private boolean abstractValue;
73 private String alias;
74 private String enterface;
75 private String pkg;
76 private String baseClass;
77 private String basePeer;
78 private Hashtable columnsByName;
79 private Hashtable columnsByJavaName;
80 private boolean needsTransactionInPostgres;
81 private boolean heavyIndexing;
82 private boolean forReferenceOnly;
83 private Map options;
84
85 /***
86 * Default Constructor
87 */
88 public Table()
89 {
90 this(null);
91 }
92
93 /***
94 * Constructs a table object with a name
95 *
96 * @param name table name
97 */
98 public Table(String name)
99 {
100 this.name = name;
101 columnList = new ArrayList();
102 foreignKeys = new ArrayList(5);
103 indices = new ArrayList(5);
104 unices = new ArrayList(5);
105 columnsByName = new Hashtable();
106 columnsByJavaName = new Hashtable();
107 options = Collections.synchronizedMap(new ListOrderedMap());
108 }
109
110 /***
111 * Load the table object from an xml tag.
112 *
113 * @param attrib xml attributes
114 * @param defaultIdMethod defined at db level
115 */
116 public void loadFromXML(Attributes attrib, String defaultIdMethod)
117 {
118 name = attrib.getValue("name");
119 javaName = attrib.getValue("javaName");
120 idMethod = attrib.getValue("idMethod");
121
122
123
124 javaNamingMethod = attrib.getValue("javaNamingMethod");
125 if (javaNamingMethod == null)
126 {
127 javaNamingMethod = getDatabase().getDefaultJavaNamingMethod();
128 }
129
130 if ("null".equals(idMethod))
131 {
132 idMethod = defaultIdMethod;
133 }
134 skipSql = "true".equals(attrib.getValue("skipSql"));
135
136 abstractValue = "true".equals(attrib.getValue("abstract"));
137 baseClass = attrib.getValue("baseClass");
138 basePeer = attrib.getValue("basePeer");
139 alias = attrib.getValue("alias");
140 heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"))
141 || (!"false".equals(attrib.getValue("heavyIndexing"))
142 && getDatabase().isHeavyIndexing());
143 description = attrib.getValue("description");
144 enterface = attrib.getValue("interface");
145 }
146
147 /***
148 * <p>A hook for the SAX XML parser to call when this table has
149 * been fully loaded from the XML, and all nested elements have
150 * been processed.</p>
151 *
152 * <p>Performs heavy indexing and naming of elements which weren't
153 * provided with a name.</p>
154 */
155 public void doFinalInitialization()
156 {
157
158
159 if (heavyIndexing)
160 {
161 doHeavyIndexing();
162 }
163
164
165
166 doNaming();
167 }
168
169 /***
170 * <p>Adds extra indices for multi-part primary key columns.</p>
171 *
172 * <p>For databases like MySQL, values in a where clause must
173 * match key part order from the left to right. So, in the key
174 * definition <code>PRIMARY KEY (FOO_ID, BAR_ID)</code>,
175 * <code>FOO_ID</code> <i>must</i> be the first element used in
176 * the <code>where</code> clause of the SQL query used against
177 * this table for the primary key index to be used. This feature
178 * could cause problems under MySQL with heavily indexed tables,
179 * as MySQL currently only supports 16 indices per table (i.e. it
180 * might cause too many indices to be created).</p>
181 *
182 * <p>See <a href="http://www.mysql.com/doc/E/X/EXPLAIN.html">the
183 * manual</a> for a better description of why heavy indexing is
184 * useful for quickly searchable database tables.</p>
185 */
186 private void doHeavyIndexing()
187 {
188 if (log.isDebugEnabled())
189 {
190 log.debug("doHeavyIndex() called on table " + name);
191 }
192
193 List pk = getPrimaryKey();
194 int size = pk.size();
195
196 try
197 {
198
199
200
201 for (int i = 1; i < size; i++)
202 {
203 addIndex(new Index(this, pk.subList(i, size)));
204 }
205 }
206 catch (EngineException e)
207 {
208 log.error(e, e);
209 }
210 }
211
212 /***
213 * Names composing objects which haven't yet been named. This
214 * currently consists of foreign-key and index entities.
215 */
216 private void doNaming()
217 {
218 int i;
219 int size;
220 String name;
221
222
223 try
224 {
225 for (i = 0, size = foreignKeys.size(); i < size; i++)
226 {
227 ForeignKey fk = (ForeignKey) foreignKeys.get(i);
228 name = fk.getName();
229 if (StringUtils.isEmpty(name))
230 {
231 name = acquireConstraintName("FK", i + 1);
232 fk.setName(name);
233 }
234 }
235
236 for (i = 0, size = indices.size(); i < size; i++)
237 {
238 Index index = (Index) indices.get(i);
239 name = index.getName();
240 if (StringUtils.isEmpty(name))
241 {
242 name = acquireConstraintName("I", i + 1);
243 index.setName(name);
244 }
245 }
246
247 for (i = 0, size = unices.size(); i < size; i++)
248 {
249 Unique unique = (Unique) unices.get(i);
250 name = unique.getName();
251 if (StringUtils.isEmpty(name))
252 {
253 name = acquireConstraintName("U", i + 1);
254 unique.setName(name);
255 }
256 }
257 }
258 catch (EngineException nameAlreadyInUse)
259 {
260 log.error(nameAlreadyInUse, nameAlreadyInUse);
261 }
262 }
263
264 /***
265 * Macro to a constraint name.
266 *
267 * @param nameType constraint type
268 * @param nbr unique number for this constraint type
269 * @return unique name for constraint
270 * @throws EngineException
271 */
272 private final String acquireConstraintName(String nameType, int nbr)
273 throws EngineException
274 {
275 List inputs = new ArrayList(4);
276 inputs.add(getDatabase());
277 inputs.add(getName());
278 inputs.add(nameType);
279 inputs.add(new Integer(nbr));
280 return NameFactory.generateName(NameFactory.CONSTRAINT_GENERATOR,
281 inputs);
282 }
283
284 /***
285 * Gets the value of base class for classes produced from this table.
286 *
287 * @return The base class for classes produced from this table.
288 */
289 public String getBaseClass()
290 {
291 if (isAlias() && baseClass == null)
292 {
293 return alias;
294 }
295 else if (baseClass == null)
296 {
297 return getDatabase().getBaseClass();
298 }
299 else
300 {
301 return baseClass;
302 }
303 }
304
305 /***
306 * Set the value of baseClass.
307 * @param v Value to assign to baseClass.
308 */
309 public void setBaseClass(String v)
310 {
311 this.baseClass = v;
312 }
313
314 /***
315 * Get the value of basePeer.
316 * @return value of basePeer.
317 */
318 public String getBasePeer()
319 {
320 if (isAlias() && basePeer == null)
321 {
322 return alias + "Peer";
323 }
324 else if (basePeer == null)
325 {
326 return getDatabase().getBasePeer();
327 }
328 else
329 {
330 return basePeer;
331 }
332 }
333
334 /***
335 * Set the value of basePeer.
336 * @param v Value to assign to basePeer.
337 */
338 public void setBasePeer(String v)
339 {
340 this.basePeer = v;
341 }
342
343 /***
344 * A utility function to create a new column from attrib and add it to this
345 * table.
346 *
347 * @param attrib xml attributes for the column to add
348 * @return the added column
349 */
350 public Column addColumn(Attributes attrib)
351 {
352 Column col = new Column();
353 col.setTable(this);
354 col.setCorrectGetters(false);
355 col.loadFromXML(attrib);
356 addColumn(col);
357 return col;
358 }
359
360 /***
361 * Adds a new column to the column list and set the
362 * parent table of the column to the current table
363 *
364 * @param col the column to add
365 */
366 public void addColumn(Column col)
367 {
368 col.setTable (this);
369 if (col.isInheritance())
370 {
371 inheritanceColumn = col;
372 }
373 columnList.add(col);
374 columnsByName.put(col.getName(), col);
375 columnsByJavaName.put(col.getJavaName(), col);
376 col.setPosition(columnList.size());
377 needsTransactionInPostgres |= col.requiresTransactionInPostgres();
378 }
379
380 /***
381 * A utility function to create a new foreign key
382 * from attrib and add it to this table.
383 *
384 * @param attrib the xml attributes
385 * @return the created ForeignKey
386 */
387 public ForeignKey addForeignKey(Attributes attrib)
388 {
389 ForeignKey fk = new ForeignKey();
390 fk.loadFromXML(attrib);
391 addForeignKey(fk);
392 return fk;
393 }
394
395 /***
396 * Gets the column that subclasses of the class representing this
397 * table can be produced from.
398 */
399 public Column getChildrenColumn()
400 {
401 return inheritanceColumn;
402 }
403
404 /***
405 * Get the objects that can be created from this table.
406 */
407 public List getChildrenNames()
408 {
409 if (inheritanceColumn == null
410 || !inheritanceColumn.isEnumeratedClasses())
411 {
412 return null;
413 }
414 List children = inheritanceColumn.getChildren();
415 List names = new ArrayList(children.size());
416 for (int i = 0; i < children.size(); i++)
417 {
418 names.add(((Inheritance) children.get(i)).getClassName());
419 }
420 return names;
421 }
422
423 /***
424 * Adds the foreign key from another table that refers to this table.
425 *
426 * @param fk A foreign key refering to this table
427 */
428 public void addReferrer(ForeignKey fk)
429 {
430 if (referrers == null)
431 {
432 referrers = new ArrayList(5);
433 }
434 referrers.add(fk);
435 }
436
437 /***
438 * Get list of references to this table.
439 *
440 * @return A list of references to this table
441 */
442 public List getReferrers()
443 {
444 return referrers;
445 }
446
447 /***
448 * Set whether this table contains a foreign PK
449 *
450 * @param b
451 */
452 public void setContainsForeignPK(boolean b)
453 {
454 containsForeignPK = b;
455 }
456
457 /***
458 * Determine if this table contains a foreign PK
459 */
460 public boolean getContainsForeignPK()
461 {
462 return containsForeignPK;
463 }
464
465 /***
466 * A list of tables referenced by foreign keys in this table
467 *
468 * @return A list of tables
469 */
470 public List getForeignTableNames()
471 {
472 if (foreignTableNames == null)
473 {
474 foreignTableNames = new ArrayList(1);
475 }
476 return foreignTableNames;
477 }
478
479 /***
480 * Adds a new FK to the FK list and set the
481 * parent table of the column to the current table
482 *
483 * @param fk A foreign key
484 */
485 public void addForeignKey(ForeignKey fk)
486 {
487 fk.setTable (this);
488 foreignKeys.add(fk);
489
490 if (foreignTableNames == null)
491 {
492 foreignTableNames = new ArrayList(5);
493 }
494 if (!foreignTableNames.contains(fk.getForeignTableName()))
495 {
496 foreignTableNames.add(fk.getForeignTableName());
497 }
498 }
499
500 /***
501 * Return true if the column requires a transaction in Postgres
502 */
503 public boolean requiresTransactionInPostgres()
504 {
505 return needsTransactionInPostgres;
506 }
507
508 /***
509 * A utility function to create a new id method parameter
510 * from attrib and add it to this table.
511 */
512 public IdMethodParameter addIdMethodParameter(Attributes attrib)
513 {
514 IdMethodParameter imp = new IdMethodParameter();
515 imp.loadFromXML(attrib);
516 addIdMethodParameter(imp);
517 return imp;
518 }
519
520
521 /***
522 * Adds a new ID method parameter to the list and sets the parent
523 * table of the column associated with the supplied parameter to this table.
524 *
525 * @param imp The column to add as an ID method parameter.
526 */
527 public void addIdMethodParameter(IdMethodParameter imp)
528 {
529 imp.setTable(this);
530 if (idMethodParameters == null)
531 {
532 idMethodParameters = new ArrayList(2);
533 }
534 idMethodParameters.add(imp);
535 }
536
537 /***
538 * Adds a new index to the index list and set the
539 * parent table of the column to the current table
540 */
541 public void addIndex(Index index)
542 {
543 index.setTable (this);
544 indices.add(index);
545 }
546
547 /***
548 * A utility function to create a new index
549 * from attrib and add it to this table.
550 */
551 public Index addIndex(Attributes attrib)
552 {
553 Index index = new Index();
554 index.loadFromXML(attrib);
555 addIndex(index);
556 return index;
557 }
558
559 /***
560 * Adds a new Unique to the Unique list and set the
561 * parent table of the column to the current table
562 */
563 public void addUnique(Unique unique)
564 {
565 unique.setTable(this);
566 unices.add(unique);
567 }
568
569 /***
570 * A utility function to create a new Unique
571 * from attrib and add it to this table.
572 *
573 * @param attrib the xml attributes
574 */
575 public Unique addUnique(Attributes attrib)
576 {
577 Unique unique = new Unique();
578 unique.loadFromXML(attrib);
579 addUnique(unique);
580 return unique;
581 }
582
583 /***
584 * Get the name of the Table
585 */
586 public String getName()
587 {
588 return name;
589 }
590
591 /***
592 * Set the name of the Table
593 */
594 public void setName(String newName)
595 {
596 name = newName;
597 }
598
599 /***
600 * Get the description for the Table
601 */
602 public String getDescription()
603 {
604 return description;
605 }
606
607 /***
608 * Set the description for the Table
609 *
610 * @param newDescription description for the Table
611 */
612 public void setDescription(String newDescription)
613 {
614 description = newDescription;
615 }
616
617 /***
618 * Get name to use in Java sources
619 */
620 public String getJavaName()
621 {
622 if (javaName == null)
623 {
624 List inputs = new ArrayList(2);
625 inputs.add(name);
626 inputs.add(javaNamingMethod);
627 try
628 {
629 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR,
630 inputs);
631 }
632 catch (EngineException e)
633 {
634 log.error(e, e);
635 }
636 }
637 return javaName;
638 }
639
640 /***
641 * Set name to use in Java sources
642 */
643 public void setJavaName(String javaName)
644 {
645 this.javaName = javaName;
646 }
647
648 /***
649 * Get the method for generating pk's
650 */
651 public String getIdMethod()
652 {
653 if (idMethod == null)
654 {
655 return IDMethod.NO_ID_METHOD;
656 }
657 else
658 {
659 return idMethod;
660 }
661 }
662
663 /***
664 * Set the method for generating pk's
665 */
666 public void setIdMethod(String idMethod)
667 {
668 this.idMethod = idMethod;
669 }
670
671 /***
672 * Skip generating sql for this table (in the event it should
673 * not be created from scratch).
674 * @return value of skipSql.
675 */
676 public boolean isSkipSql()
677 {
678 return (skipSql || isAlias() || isForReferenceOnly());
679 }
680
681 /***
682 * Set whether this table should have its creation sql generated.
683 * @param v Value to assign to skipSql.
684 */
685 public void setSkipSql(boolean v)
686 {
687 this.skipSql = v;
688 }
689
690 /***
691 * JavaName of om object this entry references.
692 * @return value of external.
693 */
694 public String getAlias()
695 {
696 return alias;
697 }
698
699 /***
700 * Is this table specified in the schema or is there just
701 * a foreign key reference to it.
702 * @return value of external.
703 */
704 public boolean isAlias()
705 {
706 return (alias != null);
707 }
708
709 /***
710 * Set whether this table specified in the schema or is there just
711 * a foreign key reference to it.
712 * @param v Value to assign to alias.
713 */
714 public void setAlias(String v)
715 {
716 this.alias = v;
717 }
718
719
720 /***
721 * Interface which objects for this table will implement
722 * @return value of interface.
723 */
724 public String getInterface()
725 {
726 return enterface;
727 }
728
729 /***
730 * Interface which objects for this table will implement
731 * @param v Value to assign to interface.
732 */
733 public void setInterface(String v)
734 {
735 this.enterface = v;
736 }
737
738 /***
739 * When a table is abstract, it marks the business object class that is
740 * generated as being abstract. If you have a table called "FOO", then the
741 * Foo BO will be <code>public abstract class Foo</code>
742 * This helps support class hierarchies
743 *
744 * @return value of abstractValue.
745 */
746 public boolean isAbstract()
747 {
748 return abstractValue;
749 }
750
751 /***
752 * When a table is abstract, it marks the business object
753 * class that is generated as being abstract. If you have a
754 * table called "FOO", then the Foo BO will be
755 * <code>public abstract class Foo</code>
756 * This helps support class hierarchies
757 *
758 * @param v Value to assign to abstractValue.
759 */
760 public void setAbstract(boolean v)
761 {
762 this.abstractValue = v;
763 }
764
765 /***
766 * Get the value of package.
767 *
768 * @return value of package.
769 */
770 public String getPackage()
771 {
772 if (pkg != null)
773 {
774 return pkg;
775 }
776 else
777 {
778 return this.getDatabase().getPackage();
779 }
780 }
781
782 /***
783 * Set the value of package.
784 *
785 * @param v Value to assign to package.
786 */
787 public void setPackage(String v)
788 {
789 this.pkg = v;
790 }
791
792 /***
793 * Returns a List containing all the columns in the table
794 *
795 * @return a List containing all the columns
796 */
797 public List getColumns()
798 {
799 return columnList;
800 }
801
802 /***
803 * Utility method to get the number of columns in this table
804 */
805 public int getNumColumns()
806 {
807 return columnList.size();
808 }
809
810 /***
811 * Returns a List containing all the FKs in the table
812 *
813 * @return a List containing all the FKs
814 */
815 public List getForeignKeys()
816 {
817 return foreignKeys;
818 }
819
820 /***
821 * Returns a Collection of parameters relevant for the chosen
822 * id generation method.
823 */
824 public List getIdMethodParameters()
825 {
826 return idMethodParameters;
827 }
828
829 /***
830 * A name to use for creating a sequence if one is not specified.
831 *
832 * @return name of the sequence
833 */
834 public String getSequenceName()
835 {
836 String result = null;
837 if (getIdMethod().equals(NATIVE))
838 {
839 List idMethodParams = getIdMethodParameters();
840 if (idMethodParams == null)
841 {
842 result = getName() + "_SEQ";
843 }
844 else
845 {
846 result = ((IdMethodParameter) idMethodParams.get(0)).getValue();
847 }
848 }
849 return result;
850 }
851
852 /***
853 * Returns a List containing all the indices in the table
854 *
855 * @return A List containing all the indices
856 */
857 public List getIndices()
858 {
859 return indices;
860 }
861
862 /***
863 * Returns a List containing all the UKs in the table
864 *
865 * @return A List containing all the UKs
866 */
867 public List getUnices()
868 {
869 return unices;
870 }
871
872 /***
873 * Returns a specified column.
874 *
875 * @param name name of the column
876 * @return Return a Column object or null if it does not exist.
877 */
878 public Column getColumn(String name)
879 {
880 return (Column) columnsByName.get(name);
881 }
882
883 /***
884 * Returns a specified column.
885 *
886 * @param javaName java name of the column
887 * @return Return a Column object or null if it does not exist.
888 */
889 public Column getColumnByJavaName(String javaName)
890 {
891 return (Column) columnsByJavaName.get(javaName);
892 }
893
894 /***
895 * Return the first foreign key that includes col in it's list
896 * of local columns. Eg. Foreign key (a,b,c) refrences tbl(x,y,z)
897 * will be returned of col is either a,b or c.
898 *
899 * @param col column name included in the key
900 * @return Return a Column object or null if it does not exist.
901 */
902 public ForeignKey getForeignKey(String col)
903 {
904 ForeignKey firstFK = null;
905 for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
906 {
907 ForeignKey key = (ForeignKey) iter.next();
908 if (key.getLocalColumns().contains(col))
909 {
910 if (firstFK == null)
911 {
912 firstFK = key;
913 }
914 else
915 {
916
917
918
919
920 }
921 }
922 }
923 return firstFK;
924 }
925
926 /***
927 * Returns true if the table contains a specified column
928 *
929 * @param col the column
930 * @return true if the table contains the column
931 */
932 public boolean containsColumn(Column col)
933 {
934 return columnList.contains(col);
935 }
936
937 /***
938 * Returns true if the table contains a specified column
939 *
940 * @param name name of the column
941 * @return true if the table contains the column
942 */
943 public boolean containsColumn(String name)
944 {
945 return (getColumn(name) != null);
946 }
947
948 /***
949 * Set the parent of the table
950 *
951 * @param parent the parant database
952 */
953 public void setDatabase(Database parent)
954 {
955 tableParent = parent;
956 }
957
958 /***
959 * Get the parent of the table
960 *
961 * @return the parant database
962 */
963 public Database getDatabase()
964 {
965 return tableParent;
966 }
967
968 /***
969 * Flag to determine if code/sql gets created for this table.
970 * Table will be skipped, if return true.
971 * @return value of forReferenceOnly.
972 */
973 public boolean isForReferenceOnly()
974 {
975 return forReferenceOnly;
976 }
977
978 /***
979 * Flag to determine if code/sql gets created for this table.
980 * Table will be skipped, if set to true.
981 * @param v Value to assign to forReferenceOnly.
982 */
983 public void setForReferenceOnly(boolean v)
984 {
985 this.forReferenceOnly = v;
986 }
987
988 /***
989 * Returns a XML representation of this table.
990 *
991 * @return XML representation of this table
992 */
993 public String toString()
994 {
995 StringBuffer result = new StringBuffer();
996
997 result.append ("<table name=\"")
998 .append(name)
999 .append('\"');
1000
1001 if (javaName != null)
1002 {
1003 result.append(" javaName=\"")
1004 .append(javaName)
1005 .append('\"');
1006 }
1007
1008 if (idMethod != null)
1009 {
1010 result.append(" idMethod=\"")
1011 .append(idMethod)
1012 .append('\"');
1013 }
1014
1015 if (skipSql)
1016 {
1017 result.append(" skipSql=\"")
1018 .append(new Boolean(skipSql))
1019 .append('\"');
1020 }
1021
1022 if (abstractValue)
1023 {
1024 result.append(" abstract=\"")
1025 .append(new Boolean(abstractValue))
1026 .append('\"');
1027 }
1028
1029 if (baseClass != null)
1030 {
1031 result.append(" baseClass=\"")
1032 .append(baseClass)
1033 .append('\"');
1034 }
1035
1036 if (basePeer != null)
1037 {
1038 result.append(" basePeer=\"")
1039 .append(basePeer)
1040 .append('\"');
1041 }
1042
1043 result.append(">\n");
1044
1045 if (columnList != null)
1046 {
1047 for (Iterator iter = columnList.iterator(); iter.hasNext();)
1048 {
1049 result.append(iter.next());
1050 }
1051 }
1052
1053 if (foreignKeys != null)
1054 {
1055 for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
1056 {
1057 result.append(iter.next());
1058 }
1059 }
1060
1061 if (idMethodParameters != null)
1062 {
1063 Iterator iter = idMethodParameters.iterator();
1064 while (iter.hasNext())
1065 {
1066 result.append(iter.next());
1067 }
1068 }
1069
1070 result.append ("</table>\n");
1071
1072 return result.toString();
1073 }
1074
1075 /***
1076 * Returns the collection of Columns which make up the single primary
1077 * key for this table.
1078 *
1079 * @return A list of the primary key parts.
1080 */
1081 public List getPrimaryKey()
1082 {
1083 List pk = new ArrayList(columnList.size());
1084
1085 Iterator iter = columnList.iterator();
1086 while (iter.hasNext())
1087 {
1088 Column col = (Column) iter.next();
1089 if (col.isPrimaryKey())
1090 {
1091 pk.add(col);
1092 }
1093 }
1094 return pk;
1095 }
1096
1097 /***
1098 * Determine whether this table has a primary key.
1099 *
1100 * @return Whether this table has any primary key parts.
1101 */
1102 public boolean hasPrimaryKey()
1103 {
1104 return (getPrimaryKey().size() > 0);
1105 }
1106
1107 /***
1108 * Returns all parts of the primary key, separated by commas.
1109 *
1110 * @return A CSV list of primary key parts.
1111 */
1112 public String printPrimaryKey()
1113 {
1114 return printList(columnList);
1115 }
1116
1117 /***
1118 * Returns the elements of the list, separated by commas.
1119 *
1120 * @param list a list of Columns
1121 * @return A CSV list.
1122 */
1123 private String printList(List list)
1124 {
1125 StringBuffer result = new StringBuffer();
1126 boolean comma = false;
1127 for (Iterator iter = list.iterator(); iter.hasNext();)
1128 {
1129 Column col = (Column) iter.next();
1130 if (col.isPrimaryKey())
1131 {
1132 if (comma)
1133 {
1134 result.append(',');
1135 }
1136 else
1137 {
1138 comma = true;
1139 }
1140 result.append(col.getName());
1141 }
1142 }
1143 return result.toString();
1144 }
1145
1146 /***
1147 * Force all columns to set the correctGetters property.
1148 *
1149 * @param value The new value of the correctGetters property.
1150 * @since 3.2
1151 */
1152 public void setCorrectGetters(Boolean value)
1153 {
1154 boolean correctGetters = value != null && value.booleanValue();
1155 for (Iterator it = columnList.iterator(); it.hasNext();)
1156 {
1157 Column col = (Column) it.next();
1158 col.setCorrectGetters(correctGetters);
1159 }
1160 }
1161
1162 /***
1163 * Add an XML Specified option key/value pair to this element's option set.
1164 *
1165 * @param key the key of the option.
1166 * @param value the value of the option.
1167 */
1168 public void addOption(String key, String value)
1169 {
1170 options.put(key, value);
1171 }
1172
1173 /***
1174 * Get the value that was associated with this key in an XML option
1175 * element.
1176 *
1177 * @param key the key of the option.
1178 * @return The value for the key or a null.
1179 */
1180 public String getOption(String key)
1181 {
1182 return (String) options.get(key);
1183 }
1184
1185 /***
1186 * Gets the full ordered hashtable array of items specified by XML option
1187 * statements under this element.<p>
1188 *
1189 * Note, this is not thread save but since it's only used for
1190 * generation which is single threaded, there should be minimum
1191 * danger using this in Velocity.
1192 *
1193 * @return An Map of all options. Will not be null but may be empty.
1194 */
1195 public Map getOptions()
1196 {
1197 return options;
1198 }
1199 }